package top.fangwenmagician.calculationstring;

import top.fangwenmagician.calculationstring.log.I_CalculationLog;
import top.fangwenmagician.calculationstring.operator.I_Operator;

import java.util.*;

/**
 * 运算符组，用来存储同一优先级的运算符
 */
class OperatorGroup {
    /**
     * 运算符组优先级
     */
    private final int priority;
    /**
     * 计算所使用的{@link CalculationString}对象，可用于回调函数
     */
    private final CalculationString calculationString;
    /**
     * 运算符键值对，key为正则表达式，value为运算符对象
     */
    private final LinkedList<I_Operator> operatorList;

    /**
     * 构造函数
     *
     * @param calculationString 计算所使用的{@link CalculationString}对象，可用于回调函数
     * @param priority          运算符组优先级
     */
    OperatorGroup(CalculationString calculationString, int priority) {
        this.calculationString = calculationString;
        this.priority = priority;
        operatorList = new LinkedList<>();
    }

    /**
     * 添加运算符
     *
     * @param operator 运算符
     * @param <T>      接口{@link I_Operator}的子类
     */
    <T extends I_Operator> void addOperator(T operator) {
        operatorList.add(operator);
    }

    /**
     * 计算表达式，同时写入日志
     *
     * @param expression 表达式
     * @param log        日志对象，需要继承{@link I_CalculationLog}接口
     * @param <T>        需要继承{@link I_CalculationLog}接口
     * @return 计算结果
     * @throws Exception 计算中抛出的异常
     */
    <T extends I_CalculationLog> String calculation(String expression, T log) throws Exception {
        if (operatorList.size() > 1) {
            return calculationOperatorList(expression, log);
        } else {
            return calculationOperator(expression, log, operatorList.getFirst());
        }
    }

    /**
     * 当运算符组里只有一个运算符时，使用该函数进行计算
     *
     * @param expression 表达式
     * @param log        日志对象，需要继承{@link I_CalculationLog}接口
     * @param operator   运算符
     * @param <T>        需要继承{@link I_CalculationLog}接口
     * @return 计算结果
     * @throws Exception 计算中抛出的异常
     */
    private <T extends I_CalculationLog> String calculationOperator(String expression, T log, I_Operator operator) throws Exception {
        if (operator.isExist(expression)) {
            expression = operator.calculation(expression, new ProcessControl(calculationString, log));
            return calculationOperator(expression, log, operator);
        } else {
            return expression;
        }
    }

    /**
     * 当运算符组里有多个运算符时，使用该函数进行计算
     *
     * @param expression 表达式
     * @param log        日志对象，需要继承{@link I_CalculationLog}接口
     * @param <T>        需要继承{@link I_CalculationLog}接口
     * @return 计算结果
     * @throws Exception 计算中抛出的异常
     */
    private <T extends I_CalculationLog> String calculationOperatorList(String expression, T log) throws Exception {
        I_Operator maxOperator = getMaxOperator(expression);
        if (maxOperator == null) {
            return expression;
        } else {
            expression = maxOperator.calculation(expression, new ProcessControl(calculationString, log));
            return calculationOperatorList(expression, log);
        }
    }

    /**
     * 获取同级优先度最大的运算符
     *
     * @param expression 表达式
     * @return 同级优先度最大的运算符
     * @throws Exception 可能出现的错误
     */
    private I_Operator getMaxOperator(String expression) throws Exception {
        I_Operator maxOperator = null;
        int maxPeerPriority = 0;
        for (I_Operator operator : operatorList) {
            if (operator.isExist(expression)) {
                if (maxOperator == null) {
                    maxOperator = operator;
                    maxPeerPriority = operator.peerPriority(expression);
                } else {
                    int peerPriority = operator.peerPriority(expression);
                    if (peerPriority > maxPeerPriority) {
                        maxOperator = operator;
                        maxPeerPriority = peerPriority;
                    }
                }
            }
        }
        return maxOperator;
    }


    int getPriority() {
        return priority;
    }
}
